package org.luxor.cloud.gateway.component.route;

import org.apache.commons.lang3.StringUtils;
import org.luxor.cloud.gateway.entity.GatewayRouteEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;

import java.net.URI;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.springframework.cloud.gateway.support.NameUtils.normalizeFilterFactoryName;
import static org.springframework.cloud.gateway.support.NameUtils.normalizeRoutePredicateName;

/**
 * 路由定义转换器
 *
 * @author Mr.Yan  @date: 2020/11/7
 */
public class RouteTransform {
    private static final Logger logger = LoggerFactory.getLogger(RouteTransform.class);

    private static final Pattern PATH_PATTERN = Pattern.compile("([a-z][a-z0-9+\\-.]*:(//[^/?#]+)?)?");
    private static final ZoneId ZONE_ID = ZoneId.of("Asia/Shanghai");
    private static final String SLASH = "/";
    private static final String FROM_IN = "Y";
    private static final String FROM = "X-Auth0-Token";
    private static final String DEFAULT_ROUT_METHOD = "GET,POST,DELETE,PUT,PATCH";
    private static final String DEFAULT_ROUT_VERSION = "v1";
    private static final String DEFAULT_TARGET_ID = "luxor-cloud-gateway";
    public static final String PATH_ROUTE_PREDICATE_NAME = normalizeRoutePredicateName(PathRoutePredicateFactory.class);

    /**
     * 转换成路由实体
     *
     * @param routeDefinition 路由定义
     * @return 路由实体
     */
    public static GatewayRouteEntity toGatewayRouteEntity(RouteDefinition routeDefinition) {
        GatewayRouteEntity routeEntity = new GatewayRouteEntity();
        routeEntity.setRouteId(Long.parseLong(routeDefinition.getId()));
        List<PredicateDefinition> predicates = routeDefinition.getPredicates();
        PredicateDefinition pathPredicate = predicates.stream().filter(p -> PATH_ROUTE_PREDICATE_NAME.equals(p.getName())).findFirst().get();
        routeEntity.setRoutePath(pathPredicate.getArgs().get(PathRoutePredicateFactory.PATTERN_KEY));
        routeEntity.setRouteMethod(DEFAULT_ROUT_METHOD);
        routeEntity.setRouteVersion(DEFAULT_ROUT_VERSION);
        routeEntity.setTargetId(DEFAULT_TARGET_ID);
        routeEntity.setTargetUri(routeDefinition.getUri().toString());
        routeEntity.setOrderNum(routeDefinition.getOrder());
        routeEntity.setStatus((byte) 0);
        routeEntity.setAuthEnabled(false);
        routeEntity.setValidEnabled(false);
        return routeEntity;
    }

    /**
     * 转换成路由定义
     *
     * @param routeEntity 路由实体
     * @return 路由定义
     */
    public static RouteDefinition toRouteDefinition(GatewayRouteEntity routeEntity) {
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId(String.valueOf(routeEntity.getRouteId()));
        routeDefinition.setUri(URI.create(routeEntity.getTargetUri()));
        routeDefinition.setOrder(routeEntity.getOrderNum());
        routeDefinition.setPredicates(routePredicates(routeEntity));
        routeDefinition.setFilters(routeFilters(routeEntity));

        if (logger.isDebugEnabled()) {
            logger.debug("toRouteDefinition: {}", routeEntity);
        }
        return routeDefinition;
    }

    /**
     * 路由断言集合
     */
    private static List<PredicateDefinition> routePredicates(GatewayRouteEntity routeEntity) {
        // 1、配置路由的断言
        List<PredicateDefinition> predicates = new ArrayList<>();
        // 1.1、Path 断言
        PredicateDefinition pathPredicate = new PredicateDefinition();
        pathPredicate.setName(normalizeRoutePredicateName(PathRoutePredicateFactory.class));
        String fullRoutePath = getFullRoutePath(routeEntity.getRouteVersion(), routeEntity.getRoutePath());
        pathPredicate.addArg(PathRoutePredicateFactory.PATTERN_KEY, fullRoutePath);
        predicates.add(pathPredicate);
        // 1.2、Method 断言
        PredicateDefinition methodPredicate = new PredicateDefinition();
        methodPredicate.setName(normalizeRoutePredicateName(MethodRoutePredicateFactory.class));
        methodPredicate.addArg(MethodRoutePredicateFactory.METHOD_KEY, routeEntity.getRouteMethod());
        predicates.add(methodPredicate);
        // 如果启用了商业限制
        if (routeEntity.getValidEnabled()) {
            Date effectTime = routeEntity.getValidEffectTime();
            Date expireTime = routeEntity.getValidExpireTime();
            if (effectTime != null && expireTime != null) {
                // 1.3、Between 断言
                PredicateDefinition betweenPredicate = new PredicateDefinition();
                betweenPredicate.setName(normalizeRoutePredicateName(BetweenRoutePredicateFactory.class));
                ZonedDateTime effectZoneDateTime = ZonedDateTime.ofInstant(effectTime.toInstant(), ZONE_ID);
                ZonedDateTime expireZoneDateTime = ZonedDateTime.ofInstant(expireTime.toInstant(), ZONE_ID);
                betweenPredicate.addArg(BetweenRoutePredicateFactory.DATETIME1_KEY, effectZoneDateTime.toString());
                betweenPredicate.addArg(BetweenRoutePredicateFactory.DATETIME2_KEY, expireZoneDateTime.toString());
                predicates.add(betweenPredicate);
            }
        }
        return predicates;
    }

    /**
     * 路由过滤器集合
     */
    private static List<FilterDefinition> routeFilters(GatewayRouteEntity routeEntity) {
        // 2、配置路由的过滤器
        List<FilterDefinition> filters = new ArrayList<>();
        // 2.1 AddRequestHeader
        FilterDefinition addRequestHeaderFilter = new FilterDefinition();
        addRequestHeaderFilter.setName(normalizeFilterFactoryName(AddRequestHeaderGatewayFilterFactory.class));
        addRequestHeaderFilter.addArg(GatewayFilter.NAME_KEY, FROM);
        addRequestHeaderFilter.addArg(GatewayFilter.VALUE_KEY, FROM_IN);
        filters.add(addRequestHeaderFilter);
        // 2.2 RewritePath
        FilterDefinition rewritePathFilter = new FilterDefinition();
        rewritePathFilter.setName(normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
        String fullRoutePath = getFullRoutePath(routeEntity.getRouteVersion(), routeEntity.getRoutePath());
        rewritePathFilter.addArg(RewritePathGatewayFilterFactory.REGEXP_KEY, fullRoutePath);
        rewritePathFilter.addArg(RewritePathGatewayFilterFactory.REPLACEMENT_KEY, extractTargetPath(routeEntity.getTargetUri()));
        filters.add(rewritePathFilter);
        // 2.3 RateLimiter
        //FilterDefinition rateLimiter = new FilterDefinition();
        //rateLimiter.setName(normalizeFilterFactoryName(RequestRateLimiterGatewayFilterFactory.class));
        //rateLimiter.addArg(RequestRateLimiterGatewayFilterFactory.NAME_KEY, "");
        //rateLimiter.addArg(RequestRateLimiterGatewayFilterFactory.VALUE_KEY, extractTargetPath(routeEntity.getTargetUri()));
        //filters.add(rateLimiter);
        // 2.4 Hystrix
        //FilterDefinition hystrixFilter = new FilterDefinition();
        //hystrixFilter.setName(normalizeFilterFactoryName(HystrixGatewayFilterFactory.class));
        //hystrixFilter.addArg(HystrixGatewayFilterFactory.NAME_KEY, "fallbackcmd");
        //hystrixFilter.addArg(HystrixGatewayFilterFactory.VALUE_KEY, extractTargetPath(routeEntity.getTargetUri()));
        //filters.add(hystrixFilter);
        return filters;
    }

    /**
     * @param targetUri 目标接口
     * @return 目标接口路径
     */
    private static String extractTargetPath(String targetUri) {
        String targetPath = null;
        Matcher matcher = PATH_PATTERN.matcher(targetUri);
        if (matcher.find()) {
            targetPath = targetUri.replace(matcher.group(1), "");
        }
        if (StringUtils.isBlank(targetPath)) {
            return "/";
        }
        return targetPath;
    }

    /**
     * @param version   路由版本
     * @param routePath 路径地址
     * @return 带版本的路由路径
     */
    private static String getFullRoutePath(String version, String routePath) {
        if (routePath.startsWith(SLASH)) {
            routePath = routePath.substring(1);
        }
        if (StringUtils.isBlank(version)) {
            return routePath;
        }
        return String.format("/%s/%s", version, routePath);
    }
}
